package com.smart.home.service.impl;

import com.smart.home.constant.UsersConstant;

import com.smart.home.context.UserContext;
import com.smart.home.dto.User.UserLoginDto;
import com.smart.home.dto.User.UserRegisterDto;
import com.smart.home.entity.Users;
import com.smart.home.exception.BusinessException;
import com.smart.home.mapper.UserMapper;
import com.smart.home.properties.JwtProperties;
import com.smart.home.service.UserService;
import com.smart.home.utils.DeviceFingerprintUtil;
import com.smart.home.utils.JwtUtil;
import com.smart.home.utils.PasswordEncoderUtils;

import com.smart.home.vo.UserVO;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;


import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private JwtProperties jwtProperties;

    /**
     *  用户注册
     * @param userRegisterDto
     * @return Interger
     */
    @Override
    public Integer register(UserRegisterDto userRegisterDto) {
         log.info("用户注册,参数:{}",userRegisterDto);

         String users1 =userMapper.SelectByUsername1(userRegisterDto.getUsername());
        if(users1 != null ){
            throw new RuntimeException(UsersConstant.USER_EXISTS);
        }
        String email =userMapper.SelectByEmail1(userRegisterDto.getEmail());
        if(email != null ){
            throw new RuntimeException(UsersConstant.EMAIL_EXISTS);
        }

         Users users = new Users();
         BeanUtils.copyProperties(userRegisterDto,users);

         String rawPassword = userRegisterDto.getPassword();
         String encryption = PasswordEncoderUtils.Encryption(rawPassword);
         users.setPassword(encryption);


         return userMapper.insert(users);
    }

    /**
     * 用户登录
     *
     * @param userLoginDto
     * @return Users
     */
    @Override
    public Users login(UserLoginDto userLoginDto) {
        final Integer MAX_LOGIN_FAIL = 5;

        //通过redis判断是否多次登入失败（防碰撞）
        log.info("用户登录,参数:{}",userLoginDto);
        String failKey = "login_fail:" + userLoginDto.getUsername();


        Integer failCount =(Integer) redisTemplate.opsForValue().get(failKey);
        if (failCount == null) {
            failCount = 0;
        }

        if (failCount >= MAX_LOGIN_FAIL) {
            throw new BusinessException(UsersConstant.TOO_MANY_LOGIN_FAILURES);
        }

        // 查询用户是否存在
        Users users = userMapper.SelectByUsername(userLoginDto.getUsername());
        if (users == null) {
            throw new BusinessException(UsersConstant.USER_NOT_FOUND);
        }

        // 检查用户状态
        if(users.getStatus() == 0){
            throw new BusinessException(UsersConstant.USER_DISABLED);
        }

        // 密码校验，并记入相关失败次数
        if(!PasswordEncoderUtils.matches(userLoginDto.getPassword(),users.getPassword())){
                redisTemplate.opsForValue().increment(failKey,1);
                redisTemplate.expire(failKey,40, TimeUnit.MINUTES);
                throw new BusinessException(UsersConstant.INVALID_USERNAME_OR_PASSWORD);
        }
        redisTemplate.delete(failKey);



        return users;
    }

    /**
     * 用户登出
     * @param request
     */
    @Override
    public void logout(HttpServletRequest request) {
        //或许token,并将token未过期的加入黑名单，以防被获取
        String token = request.getHeader(jwtProperties.getAdminTokenName());
        String fingerprint = DeviceFingerprintUtil.getFingerprint(request);

        if(token == null){
            log.info("请求头为空");
            throw new RuntimeException(UsersConstant.TOKEN_IS_NULL);
        }
        try {
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(),token);

            long l = claims.get("id",Long.class);
            String blackListKey = UsersConstant.USER_REDIS_TOKEN_KEY+l+":"+fingerprint;

            Boolean delete = redisTemplate.delete(blackListKey);
            if(delete){
                log.info("用户token缓存已经删除");
            }

            //用户登出token弃用
           long tokenTime = (claims.getExpiration().getTime()-System.currentTimeMillis())/1000;
           if(tokenTime > 0){
                String blacklistKey = UsersConstant.USER_TOKEN_BLACKLIST+claims.get("id",Long.class)+":"+fingerprint;
                redisTemplate.opsForValue().set(blacklistKey,token,tokenTime,TimeUnit.SECONDS);
           }
           log.info("用户登出成功");

        }catch (Exception e) {
            log.error("退出登录处理异常: {}", e.getMessage());
            throw new BusinessException("退出登录失败");
        }


    }


    @Override
    public void UpdatePassword(String oldPassword, String newPassword) {
        Long userId = UserContext.getUserId();
        String usersOldPassword =userMapper.QueryPasswordById(userId);

        if(usersOldPassword == null){
            throw new BusinessException(UsersConstant.USER_NOT_FOUND);
        }

        if(usersOldPassword != oldPassword){
            throw new BusinessException(UsersConstant.INVALID_USERNAME_OR_PASSWORD);
        }

        if(newPassword != null && newPassword.length() > 0){
            throw new RuntimeException(UsersConstant.NEW_PASSWORD_IS_NULL);
        }
        userMapper.UpdatePassword(newPassword,userId);
        log.info("修改密码成功");

    }

    /**
     * 返回用户个人信息
     * @param id
     * @return UserVO
     */
    @Override
    public UserVO queryUserInfo(Long id) {
        return userMapper.selectUserInfo(id);
    }


}
